package rtka

import (
	"dgo/goutils/gis"
	"dgo/goutils/gnss"
	"fmt"
	"gitee.com/ymofen/golog"
	"gitee.com/ymofen/sputils"
	"gnssa/pkg/rtkacaster"
	"os"
	"os/exec"
	"strings"
	"time"
)

type rtkDataChannel struct {
	id       string
	subconf  string
	xyz      [3]float64
	inputSub *sputils.SpBaseSub
	dec      *gnss.Rtcm3RawBufDecoder
}

func (this *rtkDataChannel) onRtcmBuf(rtcmBuf []byte) {
	msgid, _, _, _, _ := gnss.ExtractInfoFromRtcmBuf(rtcmBuf, 24)
	if msgid == 1005 || msgid == 1006 {
		var sta gnss.GnssStation
		gnss.DecodeRtcm1005V1(&sta, rtcmBuf, 24)
		this.xyz = sta.Pos
	}

}

func (this *rtkDataChannel) init() {
	this.dec = gnss.NewRtcm32RawBufDecoder(this.id)
	this.dec.OnRtcmBuf = this.onRtcmBuf
}

type RtAChannel struct {
	id string

	channel1 rtkDataChannel
	channel2 rtkDataChannel

	logger golog.Logger
}

var (
	RtkCoreExecuteName string = "./bin/rtkcore_linux"
)

func NewRtkAChannel(id string, input1, input2 string) *RtAChannel {
	rval := &RtAChannel{}
	rval.id = id
	rval.channel1.subconf = input1
	rval.channel2.subconf = input2
	rval.channel1.init()
	rval.channel2.init()
	return rval
}

func (this *RtAChannel) onRtcmBuf(ch *rtkDataChannel, buf []byte) {
	ch.dec.InputBuf(buf)
}

func (this *RtAChannel) OnNmea(nmeaText string) {
	if strings.Contains(nmeaText, "GGA") {
		nmea := gis.NewNMEA()
		nmea.ParseText(nmeaText)
		b, l, h := nmea.GetPosiBLH()
		x1, y1, z1 := gis.PosiLatLngH2EcefXyz(b, l, h)

		{
			xyz := this.channel1.xyz
			if xyz[0] != 0 {
				e, n, u := gis.CalcuENU(xyz[0], xyz[1], xyz[2], x1, y1, z1)
				this.logger.Infof("channel1 qua=%s\tsatn=%s\tdelay=%s\tstaid=%s\tenu=%.4f %.4f %.4f", nmea.Quality, nmea.NumberOfSVs, nmea.DGPSAge, nmea.SiteNo, e, n, u)
			}
		}

		//{
		//	xyz := this.channel2.xyz
		//	if xyz[0] != 0 {
		//		e, n, u := gis.CalcuENU(xyz[0], xyz[1], xyz[2], x1, y1, z1)
		//		this.logger.Infof("channel2 enu:%.4f %.4f %.4f", e, n, u)
		//	}
		//}
	}

}

func (this *RtAChannel) onInputNotifyFunc(ch *rtkDataChannel, topic string, args ...interface{}) (ok bool) {
	action := args[0].(int)
	if action == sputils.SpTypeConnected {
		this.logger.Infof("%s connected", ch.id)
	} else if action == sputils.SpTypeConnectFail {
		this.logger.Infof("%s connect fail\terr=%v", ch.id, args[2])
	} else if action == sputils.SpTypeData {
		buf := args[1].([]byte)
		this.onRtcmBuf(ch, buf)
		err := rtkacaster.RtkACasterPub(ch.id, buf)
		if err != nil {
			this.logger.Warnf("pub fail, id=%s", ch.id)
		}
	}
	return true
}

func (this *RtAChannel) Close() error {
	this.channel1.inputSub.Close()
	this.channel2.inputSub.Close()
	return nil
}

func (this *RtAChannel) Open() error {
	this.logger = golog.Default().WithOptionEx(golog.WithOptionPrefix(this.id))
	rtkacaster.CheckStartRtkACaster()

	this.channel1.id = fmt.Sprintf("%s%.2d", this.id, 1)
	this.channel2.id = fmt.Sprintf("%s%.2d", this.id, 2)

	go this.executeRtkRunner()

	this.logger.Infof("caster\taddr=%s", rtkacaster.GetRtkACasterAddr())
	this.channel1.inputSub = sputils.NewSpBaseSub("input")
	err := this.channel1.inputSub.ReloadConfString(this.channel1.subconf, func(id, topic string, args ...interface{}) (ok bool) {
		return this.onInputNotifyFunc(&this.channel1, topic, args...)
	})
	if err != nil {
		this.logger.Error(err.Error())
	}
	this.channel2.inputSub = sputils.NewSpBaseSub("input")
	err = this.channel2.inputSub.ReloadConfString(this.channel2.subconf, func(id, topic string, args ...interface{}) (ok bool) {
		return this.onInputNotifyFunc(&this.channel2, topic, args...)
	})
	if err != nil {
		this.logger.Error(err.Error())
	}

	return nil
}

func (this *RtAChannel) executeRtkRunner() {
	// ./rtkcore_linux -s -inp1 ivff001:24362@103.143.19.54:8003/RTCM33GRCEJpro -inp2 ivff002:16515@119.96.93.125:8203/RTCM33GRCEJpro -inp3 ivff003:24565@119.96.228.250:8060/RTCM3EPH -type1 6 -type2 6 -type3 6 -out ./kpl.pos -outtype 2
	// ./rtkcore_linux -s -inp1 ivff001:24362@103.143.19.54:8003/RTCM33GRCEJpro -inp2 ivff002:16515@119.96.93.125:8203/RTCM33GRCEJpro -inp3 ivff003:24565@119.96.228.250:8060/RTCM3EPH -type1 6 -type2 6 -type3 6 -out ./kpl.pos -outtype 2
	var args []string
	casterAddr := rtkacaster.GetRtkACasterAddr()
	casterAddr = strings.ReplaceAll(casterAddr, "0.0.0.0", "127.0.0.1")
	inp1 := fmt.Sprintf("a01:pass@%s/%s", casterAddr, this.channel1.id)
	inp2 := fmt.Sprintf("a02:pass@%s/%s", casterAddr, this.channel2.id)
	inp3 := fmt.Sprintf("eph1:pass@%s/%s", casterAddr, "RTKEPH")
	outputFile := fmt.Sprintf("%s.pos", this.id)
	args = append(args, "-s", "-inp1", inp1, "-inp2", inp2, "-inp3", inp3)
	args = append(args, strings.Split(fmt.Sprintf("-type1 6 -type2 6 -type3 6 -out ./%s -outtype 2", outputFile), " ")...)
	this.logger.Infof("%s %v", RtkCoreExecuteName, args)
	cmd := exec.Command(RtkCoreExecuteName, args...)
	// cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Start()
	if err != nil {
		this.logger.Error(err.Error())
	} else {
		time.AfterFunc(time.Second*2, func() {
			MonitorFile(outputFile, func(line []byte) {
				this.OnNmea(string(line))
				// this.logger.Infof("position %s", string(line))
			}, func(err error) {
				this.logger.Error(err.Error())
			})
		})

	}
	cmd.Wait()
}
